#####################################################################
#
# WEB APP FOR MSPrint SDK
# AUTHOR: zengkai001@qq.com
# DATE: 2025-04-11
#
#####################################################################

import logging
from logging.handlers import TimedRotatingFileHandler, RotatingFileHandler
import os
import traceback
from flask import Flask, render_template, request, jsonify
import sys
from pathlib import Path
from flask_cors import CORS


# Set up logging
def setup_logging():
    # Create logs directory if it doesn't exist
    log_dir = os.path.join(os.path.dirname(__file__), 'logs')
    os.makedirs(log_dir, exist_ok=True)
    log_file = os.path.join(log_dir, 'app.log')

    # Create formatters
    file_formatter = logging.Formatter(
        '%(asctime)s - %(levelname)s - %(filename)s:%(lineno)d - %(funcName)s - %(message)s'
    )
    console_formatter = logging.Formatter(
        '%(asctime)s - %(levelname)s - %(message)s'
    )

    # Set up file handler with both time-based and size-based rotation
    file_handler = TimedRotatingFileHandler(
        log_file,
        when='midnight',
        interval=1,
        backupCount=30,
    )
    file_handler.setLevel(logging.DEBUG)
    file_handler.setFormatter(file_formatter)

    # Add size-based rotation (20MB)
    size_handler = RotatingFileHandler(
        log_file,
        maxBytes=20*1024*1024,  # 20MB
        backupCount=5
    )
    size_handler.setLevel(logging.DEBUG)
    size_handler.setFormatter(file_formatter)

    # Set up console handler
    console_handler = logging.StreamHandler()
    console_handler.setLevel(logging.INFO)
    console_handler.setFormatter(console_formatter)

    # Configure root logger
    logger = logging.getLogger()
    logger.setLevel(logging.DEBUG)
    logger.addHandler(file_handler)
    logger.addHandler(size_handler)
    logger.addHandler(console_handler)

    return logger

# Initialize logging
logger = setup_logging()

# Add the parent directory to Python path to import the SDK
sys.path.append(str(Path(__file__).parent.parent))
from PYTHON_SDK.msprint import MSPrintSDK

app = Flask(__name__)
app.secret_key = 'dev' 

CORS(app, resources={
    r"/*": {
        "origins": "*",
        "methods": ["GET", "POST", "OPTIONS"],
        "allow_headers": ["Content-Type"]
    }
})

# Global printer instance
printer = None

def init_printer():
    """Initialize the printer connection"""
    global printer
    logger.info("Starting printer initialization...")
    try:
        printer = MSPrintSDK()
        logger.debug("MSPrintSDK instance created")
        
        printer.set_device(1, "/dev/usb/lp1", 0)  # Adjust device path as needed
        logger.debug("Device set to /dev/usb/lp1")
        
        printer.set_printer_model(1)
        logger.debug("Printer model set to 1")
        
        printer.initialize()
        logger.debug("Printer initialized")
        
        printer.set_read_zk_mode(0)  # Enable Chinese character support
        logger.debug("Chinese character support enabled")
        
        logger.info("Printer initialization completed successfully")
        return True
    except Exception as e:
        logger.error(f"Printer initialization failed: {str(e)}")
        logger.error(f"Stack trace: {traceback.format_exc()}")
        return False

@app.route('/')
def index():
    """Render the main page"""
    logger.info("Serving main page")
    return render_template('index.html')

@app.route('/api/init', methods=['POST'])
def api_init():
    """Initialize printer connection"""
    logger.info("Received printer initialization request")
    try:
        if init_printer():
            logger.info("Printer initialization successful")
            return jsonify({"status": "success", "message": "Printer initialized successfully"})
        else:
            logger.error("Printer initialization failed")
            return jsonify({"status": "error", "message": "Failed to initialize printer"}), 500
    except Exception as e:
        logger.error(f"Printer initialization error: {str(e)}")
        logger.error(f"Stack trace: {traceback.format_exc()}")
        return jsonify({"status": "error", "message": str(e)}), 500

@app.route('/api/print/text', methods=['POST'])
def api_print_text():
    """Print text"""
    logger.info("Received text print request")
    
    if not printer:
        logger.error("Print text failed: Printer not initialized")
        return jsonify({"status": "error", "message": "Printer not initialized"}), 400
        
    try:
        data = request.get_json()
        if not data:
            logger.error("Print text failed: No JSON data received")
            return jsonify({"status": "error", "message": "No JSON data received"}), 400
            
        text = data.get('text', '')
        alignment = data.get('alignment', 0)
        bold = data.get('bold', 0)
        
        logger.debug(f"Print parameters - Text: {text}, Alignment: {alignment}, Bold: {bold}")
        logger.debug(f"Text encoding (UTF-8): {text.encode('utf-8')}")
        
        ret = printer.set_read_zk_mode(0)
        logger.debug(f"set_read_zk_mode result: {ret}")

        printer.set_alignment(alignment)
        printer.set_bold(bold)
        
        try:
            text_gbk = text.encode('gbk')
            logger.debug(f"Successfully encoded text to GBK")
        except UnicodeEncodeError as e:
            logger.error(f"GBK encoding failed: {str(e)}")
            return jsonify({"status": "error", "message": "Text contains characters that cannot be encoded in GBK"}), 400
            
        result = printer.print_text(text)
        
        if result == 0:
            logger.info("Text printed successfully")
            return jsonify({"status": "success", "message": "Text printed successfully"})
        else:
            logger.error(f"Print failed with code {result}")
            return jsonify({"status": "error", "message": f"Print failed with code {result}"}), 500
    except Exception as e:
        logger.error(f"Print error: {str(e)}")
        logger.error(f"Stack trace: {traceback.format_exc()}")
        return jsonify({"status": "error", "message": str(e)}), 500

@app.route('/api/print/qrcode', methods=['POST'])
def api_print_qrcode():
    """Print QR code"""
    logger.info("Received QR code print request")
    
    if not printer:
        logger.error("Print QR code failed: Printer not initialized")
        return jsonify({"status": "error", "message": "Printer not initialized"}), 400
        
    try:
        data = request.json
        qr_data = data.get('data', '')
        left_margin = data.get('left_margin', 0)
        module_size = data.get('module_size', 4)
        error_correction = data.get('error_correction', 0)
        
        logger.debug(f"QR code parameters - Data: {qr_data}, Left margin: {left_margin}, "
                  f"Module size: {module_size}, Error correction: {error_correction}")
        
        result = printer.print_qrcode(qr_data, left_margin, module_size, error_correction)
        
        if result == 0:
            logger.info("QR code printed successfully")
            return jsonify({"status": "success", "message": "QR code printed successfully"})
        else:
            logger.error(f"QR code print failed with code {result}")
            return jsonify({"status": "error", "message": f"Print failed with code {result}"}), 500
    except Exception as e:
        logger.error(f"QR code print error: {str(e)}")
        logger.error(f"Stack trace: {traceback.format_exc()}")
        return jsonify({"status": "error", "message": str(e)}), 500

@app.route('/api/feed', methods=['POST'])
def api_feed():
    """Feed paper"""
    logger.info("Received paper feed request")
    
    if not printer:
        logger.error("Paper feed failed: Printer not initialized")
        return jsonify({"status": "error", "message": "Printer not initialized"}), 400
        
    try:
        data = request.json
        lines = data.get('lines', 1)
        logger.debug(f"Feed parameters - Lines: {lines}")
        
        result = printer.feed_lines(lines)
        
        if result == 0:
            logger.info(f"Fed {lines} lines successfully")
            return jsonify({"status": "success", "message": f"Fed {lines} lines successfully"})
        else:
            logger.error(f"Paper feed failed with code {result}")
            return jsonify({"status": "error", "message": f"Feed failed with code {result}"}), 500
    except Exception as e:
        logger.error(f"Paper feed error: {str(e)}")
        logger.error(f"Stack trace: {traceback.format_exc()}")
        return jsonify({"status": "error", "message": str(e)}), 500

@app.route('/api/cut', methods=['POST'])
def api_cut():
    """Cut paper"""
    logger.info("Received paper cut request")
    
    if not printer:
        logger.error("Paper cut failed: Printer not initialized")
        return jsonify({"status": "error", "message": "Printer not initialized"}), 400
        
    try:
        data = request.json
        mode = data.get('mode', 1)
        logger.debug(f"Cut parameters - Mode: {mode}")
        
        result = printer.cut_paper(mode)
        
        if result == 0:
            logger.info("Paper cut successfully")
            return jsonify({"status": "success", "message": "Paper cut successfully"})
        else:
            logger.error(f"Paper cut failed with code {result}")
            return jsonify({"status": "error", "message": f"Cut failed with code {result}"}), 500
    except Exception as e:
        logger.error(f"Paper cut error: {str(e)}")
        logger.error(f"Stack trace: {traceback.format_exc()}")
        return jsonify({"status": "error", "message": str(e)}), 500

@app.route('/api/status', methods=['POST'])
def api_status():
    """Get printer status with detailed error descriptions"""
    logger.info("Received printer status request")
    
    if not printer:
        logger.error("Status check failed: Printer not initialized")
        return jsonify({"status": "error", "message": "Printer not initialized"}), 400
        
    try:
        status = printer.get_status()
        error_messages = {
            0: "打印机正常",
            1: "打印机未连接或未上电",
            2: "打印机和调用库不匹配",
            3: "打印头打开",
            4: "切刀未复位",
            5: "打印头温度异常",
            6: "黑标错误，可能的情况为: 黑标传感器坏/使用错误白纸/黑标不标准，浓度偏低",
            7: "纸尽",
            8: "纸将尽"
        }
        status_message = error_messages.get(status, "未知状态")
        logger.info(f"Printer status: {status} - {status_message}")
        return jsonify({
            "status": "success",
            "printer_status": status,
            "status_message": status_message
        })
    except Exception as e:
        logger.error(f"Status check error: {str(e)}")
        logger.error(f"Stack trace: {traceback.format_exc()}")
        return jsonify({"status": "error", "message": str(e)}), 500

if __name__ == '__main__':
    logger.info("Starting Flask application...")
    logger.info("Server running on http://0.0.0.0:5003")
    app.run(host='0.0.0.0', port=5003, debug=True)
